﻿using AuSys.Database.Context;
using AuSys.Models;
using AuSys.Models.App;
using Microsoft.EntityFrameworkCore;

namespace AuSys.Repos
{
    /// <summary>
    /// 数据库交互类，应为Scope服务
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="context">数据库上下文</param>
    public abstract class Repo<T>(
        DataContext context
        ) : IRepo where T : class, IDbModel
    {
        protected DataContext DataContext => context;
        protected DbSet<T> Set => context.Set<T>();
        public IQueryable<T> All => Set;
        public IQueryable<T> Existing => All.Where(x => !x.Deleted);
        public T? Get(int id) => Existing.Where(x => x.Id == id).FirstOrDefault();
        public IQueryable<T> FilterRange(IQueryable<int> ids)
            => Existing.Where(x => ids.Contains(x.Id));
        public IQueryable<T> FilterRange(List<int> ids)
            => FilterRange(ids.AsQueryable());
        protected int Add(T item, bool saveChanges = true)
        {
            if (item.Id != 0)
                throw new InvalidOperationException("插入实体Id必须为0");
            WriteLastUpdate(item);
            context.Add(item);
            if (saveChanges)
                context.SaveChanges();
            return item.Id;
        }
        protected void Update(T item, bool saveChanges = true)
        {
            WriteLastUpdate(item);
            context.Update(item);
            if (saveChanges)
                context.SaveChanges();
        }
        protected void FakeRemove(T item, bool saveChanges = true)
        {
            WriteLastUpdate(item);
            item.Deleted = true;
            context.Update(item);
            if(saveChanges)
                context.SaveChanges();
        }
        protected void SaveChanges() => context.SaveChanges();

        /// <summary>
        /// 启用“最后更新时间记录”功能，适用于“多读少写”的表，启用后：<br/>
        /// 1.在增、改、删时多一步更新操作<br/>
        /// 2.可使用ReadLastUpdate获取该表最后更新时间
        /// </summary>
        protected virtual bool EnableLastUpdateRecord => false;
        private DbSet<LastUpdateRecord> UpdateRecords => context.Set<LastUpdateRecord>();
        private void WriteLastUpdate(T item)
        {
            var now = DateTime.Now;
            item.LastUpdate = now;
            PreWriteLastUpdate();
            if (!EnableLastUpdateRecord)
                return;
            var key = DbModelClassName.Value;
            var record = UpdateRecords.Find(key);
            if (record is null)
            {
                record = new() { Key = key, LastActive = now };
                UpdateRecords.Add(record);
            }
            else
                record.LastActive = now;
            //不进行SaveChanges，等待调用方SaveChanges后一并写入
        }
        protected DateTime ReadLastUpdate()
        {
            if (!EnableLastUpdateRecord)
                throw new InvalidOperationException($"未启用{nameof(EnableLastUpdateRecord)}");
            var key = DbModelClassName.Value;
            return UpdateRecords.Find(key)?.LastActive ?? new DateTime(2000, 1, 1);
        }
        protected virtual void PreWriteLastUpdate() { }
        protected static Lazy<string> DbModelClassName { get; } = new(() =>
        {
            return typeof(T).Name;
        });
    }

    public interface IRepo { }
}
